/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = wtc_mar.c                                         */
/*  NOTE      =                                                    */
/*  DATE      = 2015/08/13 by songbb                               */
/*******************************************************************/

#include <iconv2utf8.h>

#include "wtc_core.h"
#include "wtc_mar.h"
#include "wtc_comm.h"

#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))

union int_float_bits {
    int32_t int_bits;
    float float_bits;
};

union int_double_bits {
    int64_t int_bits;
    double double_bits;
};

/*******************************************************************************
*  FUNC     :  读取mbstring数据
*  ARGS     :  *p_codeset_encoding                            (MBEncoding编码)
*           :  *p_data                                  (数据入力)
*           :  *ui_data_len                             (数据入力长度)
*           :  **pp_out                                 (数据出力)
*           :  *p_ui_out_len                            (数据出力长度)
*  RTN      :  0                                        (FR_SUCC)
*              1                                        (FR_FAIL)
*  NOTE     :
*******************************************************************************/
int wtc_mar_get_mbstring(char *p_codeset_encoding
                        , char *p_data
                        , unsigned int ui_data_len
                        , char **pp_out
                        , unsigned int *p_ui_out_len)
{
    char *p_buff_out = NULL;

    if (p_codeset_encoding != NULL)
    {
        if (iconv_to_utf8(p_data, ui_data_len, p_codeset_encoding, &p_buff_out, p_ui_out_len) == FR_SUCC)
            goto success;
        wtc_free((void **) &p_buff_out);
    }

    if ((p_buff_out = (char *) frmalloc((ui_data_len + 1) * sizeof(char))) == NULL)
    {
        WTC_MALLOCK_ERR;
        goto error;
    }
    memcpy(p_buff_out, p_data, ui_data_len);
    p_buff_out[ui_data_len] = '\0';
    *p_ui_out_len = ui_data_len;

success:
    *pp_out = p_buff_out;
    return FR_SUCC;
error:
    wtc_free((void **) &p_buff_out);
    return FR_FAIL;
}

/*******************************************************************************
*  FUNC     :  读取float类型数据
*  ARGS     :  ui_msgparam                              (MsgParam)
*           :  *p_st_wtc_packet                        (WTC数据包)
*           :  *p_f_out                                 (数据出力)
*  RTN      :  0                                        (FR_SUCC)
*              1                                        (FR_FAIL)
*  NOTE     :
*******************************************************************************/
int wtc_mar_get_float(L7_wtc_packet *p_st_wtc_packet
                    , float *p_f_out)
{
    int i_tmp = 0;

    if (bytebuff_get_int(p_st_wtc_packet->p_st_packet, 4, (unsigned int *) &i_tmp, BYTE_BIG_ENDIAN) != FR_SUCC)
        return FR_FAIL;
    union int_float_bits bits;
    bits.int_bits = i_tmp;
    *p_f_out = bits.float_bits;

    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  读取double类型数据
*  ARGS     :  ui_msgparam                              (MsgParam)
*           :  *p_st_wtc_packet                        (WTC数据包)
*           :  *p_d_out                                 (数据出力)
*  RTN      :  0                                        (FR_SUCC)
*              1                                        (FR_FAIL)
*  NOTE     :
*******************************************************************************/
int wtc_mar_get_double(L7_wtc_packet *p_st_wtc_packet
                    , double *p_d_out)
{
    long long ll_tmp = 0LL;

    if (bytebuff_get_long(p_st_wtc_packet->p_st_packet, 8, (uint64_t *) &ll_tmp, BYTE_BIG_ENDIAN) != FR_SUCC)
        return FR_FAIL;
    union int_double_bits double_bits;
    double_bits.int_bits = ll_tmp;
    *p_d_out = double_bits.double_bits;

    return FR_SUCC;
}

int wtc_mar_roundup4(int i)
{
    return (i + 3) & 0xFFFFFFFC;
}

int wtc_detect_use_old_protocol_by_string(L7_wtc_packet *p_st_wtc_packet)
{
    uint64_t uint64_tmp = 0LL;
    uint8_t c_data = 0;

    if (p_st_wtc_packet->i_use_old_protocol != APM_WTC_FLAG_UNSET)
        return FR_SUCC;

    if (bytebuff_peek_long(p_st_wtc_packet->p_st_packet, 4, &uint64_tmp, BYTE_BIG_ENDIAN) != FR_SUCC)
        goto error;

    if (uint64_tmp == 0)
        goto error;
    else
    {
        if (bytebuffer_get_at(p_st_wtc_packet->p_st_packet, bytebuff_getPosition(p_st_wtc_packet->p_st_packet) + 4, &c_data) != FR_SUCC)
            goto error;
        if (c_data == 0x00)
            p_st_wtc_packet->i_use_old_protocol = FR_YES;
        else
            p_st_wtc_packet->i_use_old_protocol = FR_NO;
    }

    return FR_SUCC;
error:
    return FR_FAIL;
}

int wtc_mar_decode_vector_string(L7_wtc_packet *p_st_wtc_packet
                                , char *p_vector
                                , int i_vector_size
                                , char **pp_out
                                , unsigned int *p_ui_out_len)
{
    int i_newlen = -1;
    int i_lcv;
    unsigned int ui_tmp = 0;
    char *p_out = NULL;

    for (i_lcv = 0; i_lcv < i_vector_size; i_lcv++)
    {
        if (bytebuff_get_int(p_st_wtc_packet->p_st_packet, 4, &ui_tmp, BYTE_BIG_ENDIAN) != FR_SUCC)
            goto error;
        p_vector[i_lcv] = (char) (ui_tmp & 0xFF);
        if (i_newlen == -1 && p_vector[i_lcv] == 0)
            i_newlen = i_lcv;
    }

    if (i_newlen == -1)
    {
        if ((p_out = (char *) frmalloc((i_vector_size + 1) * sizeof(char))) == NULL)
        {
            WTC_MALLOCK_ERR;
            goto error;
        }
        memcpy(p_out, p_vector, (size_t) i_vector_size);
        p_out[i_vector_size] = '\0';
        *p_ui_out_len = (unsigned int) i_vector_size;
    }
    else if (i_newlen == 0)
        *p_ui_out_len = 0;
    else
    {
        if ((p_out = (char *) frmalloc((i_newlen + 1) * sizeof(char))) == NULL)
        {
            WTC_MALLOCK_ERR;
            goto error;
        }
        memcpy(p_out, p_vector, (size_t) i_newlen);
        p_out[i_newlen] = '\0';
        *p_ui_out_len = (unsigned int) i_newlen;
    }

    *pp_out = p_out;

    return FR_SUCC;
error:
    return FR_FAIL;
}

int wtc_xdr_length_string(char *p_string
                        , unsigned int ui_string_len)
{
    if (p_string == NULL)
        return 4;

    return 4 + wtc_mar_roundup4(ui_string_len);
}

int wtc_mar_decode_string(L7session_wtc *p_st_wtc_sess
                        , char *p_scratch
                        , int i_scratch_len
                        , L7_wtc_packet *p_st_wtc_packet
                        , char **pp_out
                        , unsigned int *p_ui_out_len)
{
    uint64_t uint64_len = 0LL;
    int i_length_padded = 0;
    char *p_usechar = NULL;
    int i_lcv;
    uint8_t byte;
    int i_need_free = FR_NO;

    if (bytebuff_get_long(p_st_wtc_packet->p_st_packet, 4, &uint64_len, BYTE_BIG_ENDIAN) != FR_SUCC)
        goto error;
    if (uint64_len == 0)
    {
        *pp_out = NULL;
        *p_ui_out_len = 0;
    }
    else
    {
        i_length_padded = wtc_mar_roundup4((int) uint64_len);
        if (p_scratch == NULL || (i_scratch_len < i_length_padded))
        {
            if ((p_usechar = (char *) frmalloc((i_length_padded) * sizeof(char))) == NULL)
            {
                WTC_MALLOCK_ERR;
                goto error;
            }
            i_need_free = FR_YES;
        }
        else
            p_usechar = p_scratch;

        for (i_lcv = 0; i_lcv < i_length_padded; i_lcv++)
        {
            if (bytebuff_get_char(p_st_wtc_packet->p_st_packet, &byte) != FR_SUCC)
                goto error;
            p_usechar[i_lcv] = byte;
        }
    }

    WTC_PRINT_WARN("p_default_encoding:[%s]\n", p_st_wtc_sess->p_default_encoding);
    if (wtc_mar_get_mbstring(p_st_wtc_sess->p_default_encoding, p_usechar, (unsigned int) uint64_len, pp_out, p_ui_out_len) != FR_SUCC)
        goto error;

    if (i_need_free == FR_YES)
        wtc_free((void **) &p_usechar);
    return FR_SUCC;
error:
    if (i_need_free == FR_YES)
        wtc_free((void **) &p_usechar);
    return FR_FAIL;
}


//////////////////////// for analyze direction
int wtc_remaining(unsigned int ui_payload_len
                , unsigned int *p_ui_offset)
{
    return ui_payload_len - *p_ui_offset;
}

int wtc_skip(char *p_payload
            , unsigned int ui_payload_len
            , unsigned int *p_ui_offset
            , int i_skip_len)
{
    if (*p_ui_offset + i_skip_len > ui_payload_len)
        return FR_FAIL;
    *p_ui_offset += i_skip_len;
    return FR_SUCC;
}

int wtc_get_char(char *p_payload
                , unsigned int ui_payload_len
                , unsigned int *p_ui_offset
                , char *p_c_out)
{
    if (*p_ui_offset + 1 > ui_payload_len)
        return FR_FAIL;
    *p_c_out = *(p_payload + *p_ui_offset);
    *p_ui_offset += 1;
    return FR_SUCC;
}

int wtc_get_int(char *p_payload
                , unsigned int ui_payload_len
                , unsigned int *p_ui_offset
                , unsigned int *p_ui_out)
{
    unsigned int *p_ui_tmp;

    if (*p_ui_offset + 4 > ui_payload_len)
        return FR_FAIL;

    p_ui_tmp = (unsigned int *) (p_payload + *p_ui_offset);
    *p_ui_out = (unsigned int) ntohl(*p_ui_tmp);

    *p_ui_offset += 4;

    return FR_SUCC;
}

int wtc_peek_int(char *p_payload
                , unsigned int ui_payload_len
                , unsigned int *p_ui_offset
                , unsigned int *p_ui_out)
{
    unsigned int *p_ui_tmp;

    if (*p_ui_offset + 4 > ui_payload_len)
        return FR_FAIL;

    p_ui_tmp = (unsigned int *) (p_payload + *p_ui_offset);
    *p_ui_out = (unsigned int) ntohl(*p_ui_tmp);

    return FR_SUCC;
}

int wtc_skip_int(char *p_payload
                , unsigned int ui_payload_len
                , unsigned int *p_ui_offset)
{
    return wtc_skip(p_payload, ui_payload_len, p_ui_offset, 4);
}

int wtc_skip_float(char *p_payload
                , unsigned int ui_payload_len
                , unsigned int *p_ui_offset)
{
    return wtc_skip(p_payload, ui_payload_len, p_ui_offset, 4);
}

int wtc_skip_double(char *p_payload
                    , unsigned int ui_payload_len
                    , unsigned int *p_ui_offset)
{
    return wtc_skip(p_payload, ui_payload_len, p_ui_offset, 8);
}

int wtc_skip_string(char *p_payload
                    , unsigned int ui_payload_len
                    , unsigned int *p_ui_offset)
{
    unsigned int ui_64_len = 0;
    if (wtc_get_int(p_payload, ui_payload_len, p_ui_offset, &ui_64_len) != FR_SUCC)
        goto error;
    if (ui_64_len != 0)
        *p_ui_offset += wtc_mar_roundup4((int) ui_64_len);
    return FR_SUCC;
error:
    return FR_FAIL;
}

int wtc_mar_decode_string_fad(char *p_payload
                            , unsigned int ui_payload_len
                            , unsigned int *p_ui_offset
                            , char *p_scratch
                            , int i_scratch_len
                            , char **pp_out
                            , unsigned int *p_ui_out_len)
{
    unsigned int ui_64_len = 0;
    int i_length_padded = 0;
    char *p_usechar = NULL;
    int i_lcv;
    char byte;
    int i_need_free = FR_NO;
    char *p_buff_out = NULL;

    if (wtc_get_int(p_payload, ui_payload_len, p_ui_offset, &ui_64_len) != FR_SUCC)
        goto error;

    if (ui_64_len == 0)
    {
        *pp_out = NULL;
        *p_ui_out_len = 0;
    }
    else
    {
        i_length_padded = wtc_mar_roundup4((int) ui_64_len);
        if (p_scratch == NULL || (i_scratch_len < i_length_padded))
        {
            if ((p_usechar = (char *) frmalloc((i_length_padded) * sizeof(char))) == NULL)
            {
                WTC_MALLOCK_ERR;
                goto error;
            }
            i_need_free = FR_YES;
        }
        else
            p_usechar = p_scratch;

        for (i_lcv = 0; i_lcv < i_length_padded; i_lcv++)
        {
            if (wtc_get_char(p_payload, ui_payload_len, p_ui_offset, &byte) != FR_SUCC)
                goto error;
            p_usechar[i_lcv] = byte;
        }
    }

    if ((p_buff_out = (char *) frmalloc((ui_64_len + 1) * sizeof(char))) == NULL)
    {
        WTC_MALLOCK_ERR;
        goto error;
    }
    memcpy(p_buff_out, p_usechar, ui_64_len);
    p_buff_out[ui_64_len] = '\0';

    *pp_out = p_buff_out;
    *p_ui_out_len = ui_64_len;

    if (i_need_free == FR_YES)
        wtc_free((void **) &p_usechar);
    return FR_SUCC;
error:
    if (i_need_free == FR_YES)
        wtc_free((void **) &p_usechar);
    return FR_FAIL;
}
